package logic.weapons;

import sound.AudioManager;
import logic.StatsContainer;
import logic.nodes.lod.LODNode;
import logic.ships.Ship;
import main.InitGame;
import main.listeners.ListenerContainer;

import com.jme.bounding.BoundingVolume;
import com.jme.math.Vector3f;
import com.jme.scene.Node;

import fileHandling.language.properties.WeaponStats;
import gameStates.network.IDObject;

public abstract class Weapon extends LODNode implements IDObject {
	
	private static final long serialVersionUID = 1L;
	
	protected float defaultSize = 0.04f;
	protected boolean isEnabled;
	protected int weaponID;
	
	private int clientWeaponFireID;
	private short clientShortID;
	
	protected StatsContainer stats;
	
	protected WeaponProperties properties;
	protected WeaponController weaponController;
	protected Ship ship;
	protected FireSpot fireSpot;
	protected Vector3f pointLocation;
	
	public Weapon(Node model, String path, String title, WeaponProperties properties, Ship ship) {
		super(path, title, model, ship.getIngameState());
		this.properties = properties;
		this.ship = ship;
		
		stats = new StatsContainer(WeaponStats.values());
		
		isEnabled = true;
		
		setIsCollidable(false);
		weaponController = new WeaponController(this);
		addController(weaponController);
		
		fireSpot = new FireSpot(this);
	}
	
	public abstract WeaponType getWeaponType();
	
	@Override
	public void setBound(BoundingVolume bound) {}
	
	@Override
	public boolean canMove() {	return true; }
	
	public boolean isEnabled() { return isEnabled; }
	
	public void setEnabled(boolean enable) { isEnabled = enable; }
	
	public WeaponFire fire(Vector3f direction) { return fire(direction, false); }
	
	public WeaponFire fire(Vector3f direction, boolean dummy) {
		if(!isEnabled() || (!dummy && (!isReadyToFire() || !enoughEnergy()))) return null;
		
		weaponController.weaponFired();
		ship.decreaseEnergy(properties.getRequiredEnergy());
		
		WeaponFire fire = getNewWeaponFire(direction, getFireSpotLocation(), dummy);

		playSound();
		addFiredShot();
		
		if(dummy) {
			fire.setIsCollidable(false);
			return fire;
		}
		
		assert(weaponID != 0);
		InitGame.get().getNetworkState().registerWeaponFire(weaponID, fire);
				
		return fire;
	}
	
	protected void addFiredShot() {
		stats.increaseStat(WeaponStats.FiredShots);
		updateAccuracy();
	}
	
	protected void updateAccuracy() {
		float shots = stats.getFloatValue(WeaponStats.FiredShots);
		float hits = stats.getFloatValue(WeaponStats.Hits);
		float acc = hits / shots;
		String str = acc + "";
		int dotIndex = str.lastIndexOf(".");
		int rightIndex = dotIndex + 3;
		int lastIndex = rightIndex < str.length() ? rightIndex : str.length();
		str = str.substring(0, lastIndex);
		stats.updateStat(WeaponStats.Accuracy, str);
	}
	
	protected void playSound() {
		AudioManager.get().playSound(AudioManager.SHOOT_01, getWorldTranslation());
	}
	
	public abstract WeaponFire getNewWeaponFire(Vector3f direction, Vector3f startLoc, boolean dummy);
	
	public Vector3f getFireSpotLocation() { return fireSpot.getFireSpotLocation(); }
	
	public boolean enoughEnergy() {
		return ship.getCurrentEnergy() >= properties.getRequiredEnergy();
	}
	
	public Ship getShip() { return ship; }
	
	public WeaponProperties getWeaponProperties() { return properties; }
	
	public void setPointLocation(Vector3f location) {
		pointLocation = location;
//		lookAt(localToWorld(pointLocation, new Vector3f()), Vector3f.UNIT_Y.clone());
	}
	
	public Vector3f getPointLocation() { return pointLocation; }
	
	@Override
	public float getDefaultScale() { return defaultSize; }
	
	@Override
	protected float getDistanceFactor() { return 0.5f; }
	
	public void setDefaultScale(float scale) {
		if(defaultSize == scale) return;
		defaultSize = scale;
		updateGeometryQuality();
	}
	
	@Override
	public void setID(int weaponIndex) { this.weaponID = weaponIndex; }
	
	@Override
	public int getID() { return weaponID; }
	
	public void addKill() { stats.increaseStat(WeaponStats.Kills); }
	
	public void addHit() {
		stats.increaseStat(WeaponStats.Hits);
		updateAccuracy();
	}
	
	public StatsContainer getStats() { return stats; }
	
	public void addWeaponListener(WeaponListener l) { weaponController.addWeaponListener(l); }
	
	public float getReloadTime() { return weaponController.getUpdateTime(); }
	
	public boolean dealsSplashDamage() { return getWeaponProperties().dealsSplashDamage(); }
	
	public float getSplashDamageRadius() { return getWeaponProperties().getSplashDamageRadius(); }
	
	public String getWeaponName() { return getWeaponProperties().getWeaponName(); }
	
	public boolean isReadyToFire() { return weaponController.isReadyToFire(); }
	
	@Override
	public ListenerContainer getListenerContainer(Class<?> listenerClass) {
		ListenerContainer c = weaponController.getListenerContainer(listenerClass);
		if(c != null) return c;
		return super.getListenerContainer(listenerClass);
	}
	
	public void setupClient(int clientWeaponFireID, short clientShortID) {
		this.clientWeaponFireID = clientWeaponFireID;
		this.clientShortID = clientShortID;
	}
	
	public void resetClient() {
		clientWeaponFireID = 0;
		clientShortID = 0;
	}
	
	public int getClientWeaponFireID() { return clientWeaponFireID; }
	
	public short getClientShortID() { return clientShortID; }
}